Skip to content

feat(database): support credentials rotation#707

Merged
eduardoboucas merged 7 commits into
mainfrom
feat/database-rotate
Jun 22, 2026
Merged

feat(database): support credentials rotation#707
eduardoboucas merged 7 commits into
mainfrom
feat/database-rotate

Conversation

@eduardoboucas

Copy link
Copy Markdown
Member

Changes the Netlify Database client to lazily read the NETLIFY_DB_URL environment variable. at query time so we can support credential rotation.

@eduardoboucas eduardoboucas requested a review from a team as a code owner June 18, 2026 11:36
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

getDatabase() is refactored so that NETLIFY_DB_URL is re-read on every use rather than captured once at call time. A new createRefreshingHttpClient() helper wraps the Neon HTTP client in a Proxy that caches the last connection string and rebuilds the underlying Neon client whenever it changes, intercepting both apply and get traps. A separate ensureNeonWebSocket() function centralizes the neonConfig.webSocketConstructor assignment for Node.js 22. For both the serverless and server drivers, connectionString on the returned object is now a getter backed by the same readConnectionString() call. The ServerlessDatabaseConnection interface's httpClient type is narrowed from NeonQueryFunction<any, any> to ReturnType<typeof neon>. Tests are updated to match the now-dynamic client and a new test verifies credential rotation is picked up immediately.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(database): support credentials rotation' accurately summarizes the main change—enabling lazy evaluation of NETLIFY_DB_URL for credentials rotation support.
Description check ✅ Passed The description directly relates to the changeset, explaining that the client now lazily reads NETLIFY_DB_URL at query time to support credential rotation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/database-rotate

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/database/prod/src/main.ts`:
- Around line 127-128: The NeonPool instance is being initialized with a static
connectionString value that won't refresh when credentials rotate, while
createRefreshingHttpClient correctly uses a Proxy pattern to refresh credentials
at each invocation via readConnectionString. Determine if this is intentional
behavior (since pool connections are long-lived and may not require credential
refresh) and if so, add clear comments above the NeonPool initialization
explaining this limitation and why direct pool usage won't benefit from
credential rotation. Alternatively, if the pool should support credential
rotation like httpClient does, refactor the NeonPool initialization to use a
similar lazy-refresh pattern with readConnectionString instead of the static
connectionString value from line 121.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d6c818d4-23b1-4ecd-9936-751869c77aac

📥 Commits

Reviewing files that changed from the base of the PR and between f1238cd and 6fb299e.

📒 Files selected for processing (2)
  • packages/database/prod/src/main.test.ts
  • packages/database/prod/src/main.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • netlify/blueprints (manual)

Comment thread packages/database/prod/src/main.ts
Comment thread packages/database/prod/src/main.ts Outdated
return new Proxy<NeonHttpClient>(target, {
apply: (_target, _thisArg, args: unknown[]) => (getClient() as (...callArgs: unknown[]) => unknown)(...args),
get: (_target, prop) => {
const value = (getClient() as unknown as Record<string | symbol, unknown>)[prop]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if needed here, but Reflect.get(getClient(), prop) might be safer (ref https://stackoverflow.com/a/59706174), but wether it even matters depends on the client internals

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was on the fence about it. I think in this particular case it wouldn't make a difference because the underlying client only has plain methods (no getters). But perhaps using Reflect.get is more idiomatic and future-proof, in case the client grows into a more complex shape.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 962e41d.

Comment thread packages/database/prod/src/main.ts Outdated
Comment on lines +98 to +100
const fn = (getClient() as unknown as Record<string | symbol, unknown>)[prop] as (
...callArgs: unknown[]
) => unknown

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be just this?

Suggested change
const fn = (getClient() as unknown as Record<string | symbol, unknown>)[prop] as (
...callArgs: unknown[]
) => unknown
const fn = value as (
...callArgs: unknown[]
) => unknown

Doesn't matter to me if it could - but just trying to gauge if I'm missing a reason that this should get a new reference for accessed prop

pieh
pieh previously approved these changes Jun 18, 2026
@eduardoboucas eduardoboucas merged commit 5f5acfa into main Jun 22, 2026
14 checks passed
@eduardoboucas eduardoboucas deleted the feat/database-rotate branch June 22, 2026 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants